/**
 * 
 */
package com.ihome.framework.core.event.command;

import java.util.Collection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.Phased;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;

import com.ihome.framework.core.event.IHomeEventBusHandler;
import com.ihome.framework.core.event.IHomeEventBusHandler.EventBusWorkHandler;
import com.ihome.framework.core.event.IHomeEventBusHandler.EventDto;
import com.ihome.framework.core.event.command.buffer.CommandBuffer;
import com.ihome.framework.core.event.command.buffer.CommandBuffer.CommandDto;
import com.ihome.framework.core.event.command.handler.SimpleCommandEventHandler;

/**
 * @author zww
 *
 */
@Configuration
public class CommandEventBusSpringBeanRegister implements Ordered,ApplicationContextAware{


    private transient ApplicationContext applicationContext;

    private transient Logger LOGGER = LoggerFactory.getLogger(getClass());


    public static class CommandEvent<E extends CommandDto> extends EventDto{
        private E commandDto;

        public CommandEvent() {

        }
        /**
         * @return the commandDto
         */
        public E getCommandDto() {
            return commandDto;
        }
        /**
         * @param commandDto the commandDto to set
         */
        public void setCommandDto(E commandDto) {
            this.commandDto = commandDto;
        }
    }




    /* (non-Javadoc)
     * @see org.springframework.core.Ordered#getOrder()
     */
    @Override
    public int getOrder() {
        return Integer.MIN_VALUE;
    }



    @Deprecated
    public static class CommandEventBusLifeCycleContainer implements SmartLifecycle,Phased{

        private final IHomeEventBusHandler INSTANCE;

        private volatile boolean running = false;

        private final String beanName;

        private final int phase;

        public CommandEventBusLifeCycleContainer(String beanName, IHomeEventBusHandler busHandler, int phase){
            this.INSTANCE = busHandler;
            this.beanName = beanName;
            this.phase = phase;
        }


        /* (non-Javadoc)
         * @see org.springframework.context.Lifecycle#start()
         */
        @Override
        public void start() {
            try{
                INSTANCE.afterPropertiesSet();
                this.running = true;
            }catch (Exception e) {
                throw new IllegalStateException(e.getMessage(), e);
            }

        }

        /* (non-Javadoc)
         * @see org.springframework.context.Lifecycle#stop()
         */
        @Override
        public void stop() {
            try{
                INSTANCE.destroy();
                this.running = false;
            }catch (Exception e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
        }

        /* (non-Javadoc)
         * @see org.springframework.context.Lifecycle#isRunning()
         */
        @Override
        public boolean isRunning() {
            return running;
        }

        /* (non-Javadoc)
         * @see org.springframework.context.Phased#getPhase()
         */
        @Override
        public int getPhase() {
            return phase;
        }

        /* (non-Javadoc)
         * @see org.springframework.context.SmartLifecycle#isAutoStartup()
         */
        @Override
        public boolean isAutoStartup() {
            return true;
        }

        /* (non-Javadoc)
         * @see org.springframework.context.SmartLifecycle#stop(java.lang.Runnable)
         */
        @Override
        public void stop(Runnable callback) {
            stop();
            callback.run();
        }

    }

    @Deprecated
    public static class CommandEventWorkLifeCycleContainer implements SmartLifecycle,Phased{

        private final EventBusWorkHandler INSTANCE;

        private volatile boolean running = false;

        private final String beanName;

        private final int phase;

        public CommandEventWorkLifeCycleContainer(String beanName, EventBusWorkHandler workHandler, int phase){
            this.INSTANCE = workHandler;
            this.beanName = beanName;
            this.phase = phase;
        }


        /* (non-Javadoc)
         * @see org.springframework.context.Lifecycle#start()
         */
        @Override
        public void start() {
            try{
                INSTANCE.afterPropertiesSet();
                this.running = true;
            }catch (Exception e) {
                throw new IllegalStateException(e.getMessage(), e);
            }

        }

        /* (non-Javadoc)
         * @see org.springframework.context.Lifecycle#stop()
         */
        @Override
        public void stop() {
            try{
                INSTANCE.destroy();
                this.running = false;
            }catch (Exception e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
        }

        /* (non-Javadoc)
         * @see org.springframework.context.Lifecycle#isRunning()
         */
        @Override
        public boolean isRunning() {
            return running;
        }

        /* (non-Javadoc)
         * @see org.springframework.context.Phased#getPhase()
         */
        @Override
        public int getPhase() {
            return phase;
        }

        /* (non-Javadoc)
         * @see org.springframework.context.SmartLifecycle#isAutoStartup()
         */
        @Override
        public boolean isAutoStartup() {
            return true;
        }

        /* (non-Javadoc)
         * @see org.springframework.context.SmartLifecycle#stop(java.lang.Runnable)
         */
        @Override
        public void stop(Runnable callback) {
            stop();
            callback.run();
        }

    }


    /* (non-Javadoc)
     * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    
    /* (non-Javadoc)
     * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
     */
    @Bean
    public CommandEventBusSpringBeanRegister register() throws BeansException {
        
        AutowireCapableBeanFactory arg0 = applicationContext.getAutowireCapableBeanFactory();
        Collection<AbstractCommandProcessor> processors = applicationContext.getBeansOfType(AbstractCommandProcessor.class).values();
        
        CommandBuffer[] buffers = applicationContext.getBeansOfType(CommandBuffer.class).values().toArray(new CommandBuffer[]{});
        CommandExecutor[] executors = applicationContext.getBeansOfType(CommandExecutor.class).values().toArray(new CommandExecutor[]{});
        CommandDispatch dispatch = applicationContext.getBean(CommandDispatch.class);
        
        for (AbstractCommandProcessor<?> commandProcessor : processors) {
            try{
//                SimpleCommandEventHandler commandWorkHandler =  (SimpleCommandEventHandler) commandProcessor.newHandlerInstance();
//                SimpleCommandEventBusHandler eventBusHandler = commandProcessor.newInstance();
                Class<?> clz = IHomeEventBusHandler.getSuperClassActualTypeArguments(commandProcessor.getClass(), 0);
                String eventWorkBeanName = String.format("eventWorkHandler%s",clz.getSimpleName());
                String eventBusBeanName = String.format("eventBusHandler%s",clz.getSimpleName());


                
//               SmartLifecycle eventWorkCycleContainer = new CommandEventWorkLifeCycleContainer(eventWorkBeanName, commandWorkHandler, 0);
//               SmartLifecycle eventBusCycleContainer = new CommandEventBusLifeCycleContainer(eventBusBeanName, eventBusHandler, 1);
               
               BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleCommandEventHandler.class);
               beanDefinitionBuilder.addPropertyValue("buffers", buffers);
               beanDefinitionBuilder.addPropertyValue("executors", executors);
               beanDefinitionBuilder.addPropertyValue("dispatch", dispatch);
               ((DefaultListableBeanFactory)arg0).registerBeanDefinition(eventWorkBeanName, beanDefinitionBuilder.getRawBeanDefinition());
               
               
               beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleCommandEventBusHandler.class);
               beanDefinitionBuilder.addPropertyValue("simpleCommandEventHandler", arg0.getBean(eventWorkBeanName));
               ((DefaultListableBeanFactory)arg0).registerBeanDefinition(eventBusBeanName, beanDefinitionBuilder.getRawBeanDefinition());
               commandProcessor.setCommandEventProducer((SimpleCommandEventBusHandler)arg0.getBean(eventBusBeanName));
            }catch (Exception e) {
                throw new FatalBeanException(e.getMessage(),e);
            }
        }
        return this;
   
    }


}
